/*
 * Appointment.java
 * 
 * @version 1.0, March 6, 2011
 * CS 2340 Team 10: Cyberdyne Systems
 */

package edu.gatech.oad.cyberdoc.domain;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.json.JSONException;
import org.json.JSONObject;

import edu.gatech.oad.cyberdoc.database.DatabaseSerializable;

/**
 * A class to represent a single appointment, and to provide methods to
 * manipulate this appointment and all appointments.
 * 
 * @author John Girata
 * @version 1.0, March 10, 2011
 */
public class Appointment implements DatabaseSerializable {

	/**
	 * The logger for this class.
	 */
	private static final Logger LOG;

	/**
	 * Maps all appointments from their unique ID numbers to their instances.
	 */
	private static final Map<Integer, Appointment> APPOINTMENTS;

	/**
	 * This appointments properties.
	 */
	private AppointmentSpec spec;

	/**
	 * This appointment's unique ID number.
	 */
	private final int id;

	static {
		APPOINTMENTS = new HashMap<Integer, Appointment>();
	}

	static {
		LOG = Logger
				.getLogger("edu.gatech.oad.cyberdoc.database.FlatFileDatabase");
	}

	/**
	 * Creates a new appointment.
	 * 
	 * @param id
	 *            The appointment's unique ID
	 * @param spec
	 *            The appointment's properties
	 */
	public Appointment(int id, AppointmentSpec spec) {
		this.id = id;
		this.spec = spec;

		APPOINTMENTS.put(id, this);
	}

	/**
	 * Creates an instance of this class from a JSONObject, as required by
	 * DatabaseSerializable.
	 * 
	 * @param object
	 *            The information from the database to use to create this User
	 */
	public Appointment(JSONObject object) {
		int id = 0;

		try {
			id = object.getInt("id");
			spec = new AppointmentSpec(object.getJSONObject("spec"));
		} catch (JSONException e) {
			LOG.log(Level.SEVERE, "Expected field not found", e);
		}

		this.id = id;
	}

	/**
	 * Determines if this appointment conflicts with any appointment already
	 * created.
	 * 
	 * The method uses the definition of "conflicting appointment" defined by
	 * AppointmentSpec.isConflicting().
	 * 
	 * @param spec
	 *            The AppointmentSpec to compare against
	 * @return true if this appointment is conflicting, false otherwise
	 * @see edu.gatech.oad.cyberdoc.domain.AppointmentSpec#isConflicting(AppointmentSpec)
	 */
	public static boolean isConflicting(AppointmentSpec spec) {
		for (Map.Entry<?, Appointment> entry : APPOINTMENTS.entrySet()) {
			if (entry.getValue().spec.isConflicting(spec)) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Returns a list of all of the appointments occurring on the given date.
	 * 
	 * @param date
	 *            The date in question
	 * @return The appointments occurring on that date
	 */
	public static List<Appointment> getByDate(Date date) {
		final List<Appointment> result = new ArrayList<Appointment>();

		for (Map.Entry<?, Appointment> entry : APPOINTMENTS.entrySet()) {
			Appointment appt = entry.getValue();

			if (appt.spec.isSameDate(date)) {
				result.add(appt);
			}
		}

		return result;
	}

	/**
	 * Returns an appointment based on a unique ID number.
	 * 
	 * @param id
	 *            The appointment's ID
	 * 
	 * @return The appointment corresponding to the ID
	 */
	public static Appointment getAppointment(Integer id) {
		return APPOINTMENTS.get(id);
	}

	/**
	 * Returns the appointment's current properties.
	 * 
	 * @return The Appointment's current properties
	 */
	public AppointmentSpec getSpec() {
		return spec;
	}

	/**
	 * Changes the appointment's current properties.
	 * 
	 * @param spec
	 *            This Appointment's new properties
	 */
	public void setSpec(AppointmentSpec spec) {
		this.spec = spec;
	}

	/**
	 * Returns the date associated with this appointment.
	 * 
	 * @return The date associated with this appointment, or null if it has not
	 *         been set
	 */
	public Date getDate() {
		return getSpec().getDate();
	}

	/**
	 * Returns a string representation of this Appointment.
	 * 
	 * @return A string representation of this Appointment
	 */
	@Override
	public String toString() {
		return "Appointment [spec=" + spec + ", id=" + id + "]";
	}

	/**
	 * Serializes this object into a JSONObject.
	 * 
	 * @return The JSON representation of this class
	 */
	@Override
	public JSONObject toJSONObject() {
		final JSONObject object = new JSONObject();
		
		try {
			object.put("id", id);
			object.put("spec", spec.toJSONObject());
		} catch (JSONException e) {
			LOG.log(Level.SEVERE, "Error building JSON representation", e);
		}
		
		return object;
	}

	/**
	 * Returns a string that uniquely identifies an instance of this class.
	 * 
	 * @return This instance's unique identifier
	 */
	@Override
	public String getUniqueIdentifier() {
		return "" + id;
	}
	
	/**
	 * Removes all active appointment objects.
	 */
	public static void clear() {
		APPOINTMENTS.clear();
	}
}
